{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "#### 1.2 Prompt (提示模版)\n",
    "在我们开发大模型应用时，大多数情况下不会直接将用户的输入直接传递给 LLM。通常，他们会将用户输入添加到一个较大的文本中，称为提示模板，该文本提供有关当前特定任务的附加上下文。 PromptTemplates 正是帮助解决这个问题！它们捆绑了从用户输入到完全格式化的提示的所有逻辑。这可以非常简单地开始 - 例如，生成上述字符串的提示就是："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "metadata": {},
   "outputs": [],
   "source": [
    "from langchain_core.prompts import ChatPromptTemplate\n",
    "\n",
    "# 这里我们要求模型对给定文本进行中文翻译\n",
    "prompt = \"\"\"请你将由三个反引号分割的文本翻译成英文！\\\n",
    "text: ```{text}```\n",
    "\"\"\"\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "'请你将由三个反引号分割的文本翻译成英文！text: ```我带着比身体重的行李，游入尼罗河底，经过几道闪电 看到一堆光圈，不确定是不是这里。```\\n'"
      ]
     },
     "execution_count": 2,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "text = \"我带着比身体重的行李，\\\n",
    "游入尼罗河底，\\\n",
    "经过几道闪电 看到一堆光圈，\\\n",
    "不确定是不是这里。\\\n",
    "\"\n",
    "prompt.format(text=text)\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "我们知道聊天模型的接口是基于消息（message），而不是原始的文本。PromptTemplates 也可以用于产生消息列表，在这种样例中，prompt不仅包含了输入内容信息，也包含了每条message的信息(角色、在列表中的位置等)。通常情况下，一个 ChatPromptTemplate 是一个 ChatMessageTemplate 的列表。每个 ChatMessageTemplate 包含格式化该聊天消息的说明（其角色以及内容）。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "[SystemMessage(content='你是一个翻译助手，可以帮助我将 中文 翻译成 英文.'),\n",
       " HumanMessage(content='我带着比身体重的行李，游入尼罗河底，经过几道闪电 看到一堆光圈，不确定是不是这里。')]"
      ]
     },
     "execution_count": 3,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "from langchain.prompts.chat import ChatPromptTemplate\n",
    "\n",
    "template = \"你是一个翻译助手，可以帮助我将 {input_language} 翻译成 {output_language}.\"\n",
    "human_template = \"{text}\"\n",
    "\n",
    "chat_prompt = ChatPromptTemplate.from_messages([\n",
    "    (\"system\", template),\n",
    "    (\"human\", human_template),\n",
    "])\n",
    "\n",
    "text = \"我带着比身体重的行李，\\\n",
    "游入尼罗河底，\\\n",
    "经过几道闪电 看到一堆光圈，\\\n",
    "不确定是不是这里。\\\n",
    "\"\n",
    "messages  = chat_prompt.format_messages(input_language=\"中文\", output_language=\"英文\", text=text)\n",
    "messages\n",
    "\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "#### 1.3 Output parser（输出解析器）\n",
    "OutputParsers 将语言模型的原始输出转换为可以在下游使用的格式。 OutputParsers 有几种主要类型，包括：\n",
    "\n",
    "- 将 LLM 文本转换为结构化信息（例如 JSON）\n",
    "- 将 ChatMessage 转换为字符串\n",
    "- 将除消息之外的调用返回的额外信息（如 OpenAI 函数调用）转换为字符串\n",
    "\n",
    "最后，我们将模型输出传递给 output_parser，它是一个 BaseOutputParser，这意味着它接受字符串或 BaseMessage 作为输入。 StrOutputParser 特别简单地将任何输入转换为字符串。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "\"AIMessage(content='I carried luggage heavier than my body and dived into the bottom of the Nile River. After passing through several flashes of lightning, I saw a pile of halos, not sure if this is the place.')\""
      ]
     },
     "execution_count": 6,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "from langchain_core.output_parsers import StrOutputParser\n",
    "\n",
    "output_parser = StrOutputParser()\n",
    "output_parser.invoke(output)\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "#### 1.4 完整的流程\n",
    "我们现在可以将所有这些组合成一条链。该链将获取输入变量，将这些变量传递给提示模板以创建提示，将提示传递给语言模型，然后通过（可选）输出解析器传递输出。接下来我们将使用LCEL这种语法去快速实现一条链（chain）。让我们看看它的实际效果！"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "chain = chat_prompt | llm | output_parser\n",
    "chain.invoke({\"input_language\":\"中文\", \"output_language\":\"英文\",\"text\": text})\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "##### 2.2 在 langchain 直接调用文心一言\n",
    "我们也可以使用新版 LangChain，来直接调用文心一言大模型。\n",
    "\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "metadata": {},
   "outputs": [],
   "source": [
    "\n",
    "from dotenv import find_dotenv, load_dotenv\n",
    "import os\n",
    "\n",
    "# 读取本地/项目的环境变量。\n",
    "\n",
    "# find_dotenv()寻找并定位.env文件的路径\n",
    "# load_dotenv()读取该.env文件，并将其中的环境变量加载到当前的运行环境中\n",
    "# 如果你设置的是全局的环境变量，这行代码则没有任何作用。\n",
    "_ = load_dotenv(find_dotenv())\n",
    "\n",
    "# 获取环境变量 API_KEY\n",
    "QIANFAN_AK = os.environ[\"QIANFAN_AK\"]\n",
    "QIANFAN_SK = os.environ[\"QIANFAN_SK\"]\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "metadata": {},
   "outputs": [
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "[WARNING][2024-12-04 20:29:25.644] redis_rate_limiter.py:21 [t:18860]: No redis installed, RedisRateLimiter unavailable. Ignore this warning if you don't need to use qianfan SDK in distribution environment\n",
      "C:\\Users\\Administrator\\AppData\\Local\\Temp\\ipykernel_10168\\3737551541.py:4: LangChainDeprecationWarning: The method `BaseLLM.__call__` was deprecated in langchain-core 0.1.7 and will be removed in 1.0. Use invoke instead.\n",
      "  res = llm(\"你好，请你自我介绍一下！\")\n",
      "[ERROR][2024-12-04 20:29:26.417] base.py:134 [t:18860]: http request url https://qianfan.baidubce.com/wenxinworkshop/service/list failed with http status code 403\n",
      "error code from baidu: IamSignatureInvalid\n",
      "error message from baidu: IamSignatureInvalid, cause: Could not find credential.\n",
      "request headers: {'User-Agent': 'python-requests/2.28.1', 'Accept-Encoding': 'gzip, deflate, br', 'Accept': '*/*', 'Connection': 'keep-alive', 'Content-Type': 'application/json', 'Host': 'qianfan.baidubce.com', 'request-source': 'qianfan_py_sdk_v0.4.12.1', 'x-bce-date': '2024-12-04T12:29:26Z', 'Authorization': 'bce-auth-v1//2024-12-04T12:29:26Z/300/request-source;content-type;x-bce-date;host/634c7fda9c2811afd6f7631274beece8a56b849cc5d5928a8ef3a4c69710261c', 'Content-Length': '2'}\n",
      "request body: '{}'\n",
      "response headers: {'Bfe-Trace-Id': '14056ed5cea7cf40969366f31a7bb04d', 'Content-Length': '0', 'Date': 'Wed, 04 Dec 2024 12:29:28 GMT', 'X-Bce-Error-Code': 'IamSignatureInvalid', 'X-Bce-Error-Message': 'IamSignatureInvalid, cause: Could not find credential.', 'X-Bce-Exception-Point': 'Gateway', 'X-Bce-Gateway-Region': 'BJ', 'X-Bce-Request-Id': '060dd18c-7822-4176-b1e3-47bd4c52d5bc', 'Content-Type': 'text/plain; charset=utf-8'}\n",
      "response body: b''\n",
      "[WARNING][2024-12-04 20:29:26.419] base.py:1083 [t:18860]: fetch_supported_models failed: http request url https://qianfan.baidubce.com/wenxinworkshop/service/list failed with http status code 403\n",
      "error code from baidu: IamSignatureInvalid\n",
      "error message from baidu: IamSignatureInvalid, cause: Could not find credential.\n",
      "request headers: {'User-Agent': 'python-requests/2.28.1', 'Accept-Encoding': 'gzip, deflate, br', 'Accept': '*/*', 'Connection': 'keep-alive', 'Content-Type': 'application/json', 'Host': 'qianfan.baidubce.com', 'request-source': 'qianfan_py_sdk_v0.4.12.1', 'x-bce-date': '2024-12-04T12:29:26Z', 'Authorization': 'bce-auth-v1//2024-12-04T12:29:26Z/300/request-source;content-type;x-bce-date;host/634c7fda9c2811afd6f7631274beece8a56b849cc5d5928a8ef3a4c69710261c', 'Content-Length': '2'}\n",
      "request body: '{}'\n",
      "response headers: {'Bfe-Trace-Id': '14056ed5cea7cf40969366f31a7bb04d', 'Content-Length': '0', 'Date': 'Wed, 04 Dec 2024 12:29:28 GMT', 'X-Bce-Error-Code': 'IamSignatureInvalid', 'X-Bce-Error-Message': 'IamSignatureInvalid, cause: Could not find credential.', 'X-Bce-Exception-Point': 'Gateway', 'X-Bce-Gateway-Region': 'BJ', 'X-Bce-Request-Id': '060dd18c-7822-4176-b1e3-47bd4c52d5bc', 'Content-Type': 'text/plain; charset=utf-8'}\n",
      "response body: b''\n",
      "[ERROR][2024-12-04 20:29:26.669] base.py:134 [t:18860]: http request url https://qianfan.baidubce.com/wenxinworkshop/service/list failed with http status code 403\n",
      "error code from baidu: IamSignatureInvalid\n",
      "error message from baidu: IamSignatureInvalid, cause: Could not find credential.\n",
      "request headers: {'User-Agent': 'python-requests/2.28.1', 'Accept-Encoding': 'gzip, deflate, br', 'Accept': '*/*', 'Connection': 'keep-alive', 'Content-Type': 'application/json', 'Host': 'qianfan.baidubce.com', 'request-source': 'qianfan_py_sdk_v0.4.12.1', 'x-bce-date': '2024-12-04T12:29:26Z', 'Authorization': 'bce-auth-v1//2024-12-04T12:29:26Z/300/request-source;content-type;x-bce-date;host/634c7fda9c2811afd6f7631274beece8a56b849cc5d5928a8ef3a4c69710261c', 'Content-Length': '2'}\n",
      "request body: '{}'\n",
      "response headers: {'Bfe-Trace-Id': 'd6552286417c65ef78bbc028520c665a', 'Content-Length': '0', 'Date': 'Wed, 04 Dec 2024 12:29:28 GMT', 'X-Bce-Error-Code': 'IamSignatureInvalid', 'X-Bce-Error-Message': 'IamSignatureInvalid, cause: Could not find credential.', 'X-Bce-Exception-Point': 'Gateway', 'X-Bce-Gateway-Region': 'BJ', 'X-Bce-Request-Id': 'ed61f3f9-db2d-4ed8-8686-5d5ba0ed0243', 'Content-Type': 'text/plain; charset=utf-8'}\n",
      "response body: b''\n",
      "[WARNING][2024-12-04 20:29:26.670] base.py:1083 [t:18860]: fetch_supported_models failed: http request url https://qianfan.baidubce.com/wenxinworkshop/service/list failed with http status code 403\n",
      "error code from baidu: IamSignatureInvalid\n",
      "error message from baidu: IamSignatureInvalid, cause: Could not find credential.\n",
      "request headers: {'User-Agent': 'python-requests/2.28.1', 'Accept-Encoding': 'gzip, deflate, br', 'Accept': '*/*', 'Connection': 'keep-alive', 'Content-Type': 'application/json', 'Host': 'qianfan.baidubce.com', 'request-source': 'qianfan_py_sdk_v0.4.12.1', 'x-bce-date': '2024-12-04T12:29:26Z', 'Authorization': 'bce-auth-v1//2024-12-04T12:29:26Z/300/request-source;content-type;x-bce-date;host/634c7fda9c2811afd6f7631274beece8a56b849cc5d5928a8ef3a4c69710261c', 'Content-Length': '2'}\n",
      "request body: '{}'\n",
      "response headers: {'Bfe-Trace-Id': 'd6552286417c65ef78bbc028520c665a', 'Content-Length': '0', 'Date': 'Wed, 04 Dec 2024 12:29:28 GMT', 'X-Bce-Error-Code': 'IamSignatureInvalid', 'X-Bce-Error-Message': 'IamSignatureInvalid, cause: Could not find credential.', 'X-Bce-Exception-Point': 'Gateway', 'X-Bce-Gateway-Region': 'BJ', 'X-Bce-Request-Id': 'ed61f3f9-db2d-4ed8-8686-5d5ba0ed0243', 'Content-Type': 'text/plain; charset=utf-8'}\n",
      "response body: b''\n",
      "[ERROR][2024-12-04 20:29:26.893] base.py:134 [t:18860]: http request url https://qianfan.baidubce.com/wenxinworkshop/service/list failed with http status code 403\n",
      "error code from baidu: IamSignatureInvalid\n",
      "error message from baidu: IamSignatureInvalid, cause: Could not find credential.\n",
      "request headers: {'User-Agent': 'python-requests/2.28.1', 'Accept-Encoding': 'gzip, deflate, br', 'Accept': '*/*', 'Connection': 'keep-alive', 'Content-Type': 'application/json', 'Host': 'qianfan.baidubce.com', 'request-source': 'qianfan_py_sdk_v0.4.12.1', 'x-bce-date': '2024-12-04T12:29:26Z', 'Authorization': 'bce-auth-v1//2024-12-04T12:29:26Z/300/request-source;content-type;x-bce-date;host/634c7fda9c2811afd6f7631274beece8a56b849cc5d5928a8ef3a4c69710261c', 'Content-Length': '2'}\n",
      "request body: '{}'\n",
      "response headers: {'Bfe-Trace-Id': '2fde3e0b1375fc4d84d7901ad40fc10e', 'Content-Length': '0', 'Date': 'Wed, 04 Dec 2024 12:29:28 GMT', 'X-Bce-Error-Code': 'IamSignatureInvalid', 'X-Bce-Error-Message': 'IamSignatureInvalid, cause: Could not find credential.', 'X-Bce-Exception-Point': 'Gateway', 'X-Bce-Gateway-Region': 'BJ', 'X-Bce-Request-Id': '844f5b3f-9d7d-4c21-959e-c22f2a106577', 'Content-Type': 'text/plain; charset=utf-8'}\n",
      "response body: b''\n",
      "[WARNING][2024-12-04 20:29:26.895] base.py:1083 [t:18860]: fetch_supported_models failed: http request url https://qianfan.baidubce.com/wenxinworkshop/service/list failed with http status code 403\n",
      "error code from baidu: IamSignatureInvalid\n",
      "error message from baidu: IamSignatureInvalid, cause: Could not find credential.\n",
      "request headers: {'User-Agent': 'python-requests/2.28.1', 'Accept-Encoding': 'gzip, deflate, br', 'Accept': '*/*', 'Connection': 'keep-alive', 'Content-Type': 'application/json', 'Host': 'qianfan.baidubce.com', 'request-source': 'qianfan_py_sdk_v0.4.12.1', 'x-bce-date': '2024-12-04T12:29:26Z', 'Authorization': 'bce-auth-v1//2024-12-04T12:29:26Z/300/request-source;content-type;x-bce-date;host/634c7fda9c2811afd6f7631274beece8a56b849cc5d5928a8ef3a4c69710261c', 'Content-Length': '2'}\n",
      "request body: '{}'\n",
      "response headers: {'Bfe-Trace-Id': '2fde3e0b1375fc4d84d7901ad40fc10e', 'Content-Length': '0', 'Date': 'Wed, 04 Dec 2024 12:29:28 GMT', 'X-Bce-Error-Code': 'IamSignatureInvalid', 'X-Bce-Error-Message': 'IamSignatureInvalid, cause: Could not find credential.', 'X-Bce-Exception-Point': 'Gateway', 'X-Bce-Gateway-Region': 'BJ', 'X-Bce-Request-Id': '844f5b3f-9d7d-4c21-959e-c22f2a106577', 'Content-Type': 'text/plain; charset=utf-8'}\n",
      "response body: b''\n",
      "[INFO][2024-12-04 20:29:26.898] oauth.py:277 [t:18860]: trying to refresh token for ak `enA9Ab***`\n",
      "[INFO][2024-12-04 20:29:27.442] oauth.py:304 [t:18860]: successfully refresh token\n"
     ]
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "好的，让我自我介绍一下。我是一个AI助手或计算机程序，根据你的请求我可以回答很多不同类型的问题并为你提供相应的信息或服务。请问您有任何具体的主题或需要我回答的内容吗？请随时告诉我，我会尽我所能帮助您。\n"
     ]
    }
   ],
   "source": [
    "from langchain_community.llms import QianfanLLMEndpoint\n",
    "\n",
    "llm = QianfanLLMEndpoint(streaming=True)\n",
    "res = llm(\"你好，请你自我介绍一下！\")\n",
    "print(res)\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "##### 3. 使用 LangChain 调用讯飞星火"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 9,
   "metadata": {},
   "outputs": [],
   "source": [
    "from dotenv import find_dotenv, load_dotenv\n",
    "import os\n",
    "\n",
    "# 读取本地/项目的环境变量。\n",
    "\n",
    "# find_dotenv()寻找并定位.env文件的路径\n",
    "# load_dotenv()读取该.env文件，并将其中的环境变量加载到当前的运行环境中\n",
    "# 如果你设置的是全局的环境变量，这行代码则没有任何作用。\n",
    "_ = load_dotenv(find_dotenv())\n",
    "\n",
    "# 获取环境变量 API_KEY\n",
    "IFLYTEK_SPARK_APP_ID = os.environ[\"IFLYTEK_SPARK_APP_ID\"]\n",
    "IFLYTEK_SPARK_API_KEY = os.environ[\"IFLYTEK_SPARK_API_KEY\"]\n",
    "IFLYTEK_SPARK_API_SECRET = os.environ[\"IFLYTEK_SPARK_API_SECRET\"]\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 13,
   "metadata": {},
   "outputs": [],
   "source": [
    "def gen_spark_params(model):\n",
    "    '''\n",
    "    构造星火模型请求参数\n",
    "    '''\n",
    "\n",
    "    spark_url_tpl = \"wss://spark-api.xf-yun.com/{}/chat\"\n",
    "    model_params_dict = {\n",
    "        # v1.5 版本\n",
    "        \"v1.5\": {\n",
    "            \"domain\": \"general\", # 用于配置大模型版本\n",
    "            \"spark_url\": spark_url_tpl.format(\"v1.1\") # 云端环境的服务地址\n",
    "        },\n",
    "        # v2.0 版本\n",
    "        \"v2.0\": {\n",
    "            \"domain\": \"generalv2\", # 用于配置大模型版本\n",
    "            \"spark_url\": spark_url_tpl.format(\"v2.1\") # 云端环境的服务地址\n",
    "        },\n",
    "        # v3.0 版本\n",
    "        \"v3.0\": {\n",
    "            \"domain\": \"generalv3\", # 用于配置大模型版本\n",
    "            \"spark_url\": spark_url_tpl.format(\"v3.1\") # 云端环境的服务地址\n",
    "        },\n",
    "        # v3.5 版本\n",
    "        \"v3.5\": {\n",
    "            \"domain\": \"generalv3.5\", # 用于配置大模型版本\n",
    "            \"spark_url\": spark_url_tpl.format(\"v3.5\") # 云端环境的服务地址\n",
    "        }\n",
    "    }\n",
    "    return model_params_dict[model]\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 14,
   "metadata": {},
   "outputs": [],
   "source": [
    "from langchain_community.llms import SparkLLM\n",
    "\n",
    "spark_api_url = gen_spark_params(model=\"v3.5\")[\"spark_url\"]\n",
    "\n",
    "# Load the model(默认使用 v3.5)\n",
    "llm = SparkLLM(spark_api_url = spark_api_url)  #指定 v3.5版本\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 15,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "您好，我是科大讯飞研发的认知智能大模型，我的名字叫讯飞星火认知大模型。我可以和人类进行自然交流，解答问题，高效完成各领域认知智能需求。\n"
     ]
    }
   ],
   "source": [
    "res = llm(\"你好，请你自我介绍一下！\")\n",
    "print(res)\n"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "jupyter38",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.8.13"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 2
}
